﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Activation;
using YasharEl.Infrastructure.ObjectFactory.ServiceModel;
using YasharEl.Infrastructure.ObjectFactory;
using System.Diagnostics.Contracts;
using YasharEl.Infrastructure.Diagnostics.Logging;
using YasharEl.Infrastructure.ProcessEngine.DomainModel.ProcessMetadata;
using YasharEl.Infrastructure.ResourcesManagement;
using YasharEl.Infrastructure.ObjectFactory.Adapters;
using YasharEl.Infrastructure.Core.ServiceModel;
using YasharEl.Infrastructure.Core.Prototype;

namespace YasharEl.Infrastructure.ProcessEngine.ServiceModel.Services
{
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    [ModuleService(ProcessEngineConstants.BaseName)]
    public partial class ProcessMetadataRetrieverService
    {
        #region ReadOnly Fields

        private readonly IDependencyResolver resolver;
        private readonly ILogger<ProcessMetadataRetrieverService> logger;
        private readonly ITypeAdapter typeAdapter;
        private readonly IProcessMetadataCatalog processMetadataCatalog;

        #endregion

        #region Constructors

        public ProcessMetadataRetrieverService(
            IDependencyResolver resolver, 
            ILogger<ProcessMetadataRetrieverService> logger,
            ITypeAdapter typeAdapter,
            IProcessMetadataCatalog processMetadataCatalog)
        {
            Contract.Requires<ArgumentNullException>(resolver != null, "resolver could not be null.");
            Contract.Requires<ArgumentNullException>(logger != null, "logger could not be null.");
            Contract.Requires<ArgumentNullException>(typeAdapter != null, "typeAdapter could not be null.");
            Contract.Requires<ArgumentNullException>(processMetadataCatalog != null, "processMetadataCatalog could not be null.");

            this.resolver = resolver;
            this.logger = logger;
            this.typeAdapter = typeAdapter;
            this.processMetadataCatalog = processMetadataCatalog;
        }

        #endregion

        #region ProcessMetadataRetrieverService Overrides

        public override RetrieveProcessDefinitionResponse RetrieveProcessDefinition(RetrieveProcessDefinitionRequest request)
        {
            logger.Debug("Retrieving process definition {0} ...", request.ProcessDefinitionId);

            RetrieveProcessDefinitionResponse response = new RetrieveProcessDefinitionResponse()
            {
                DefinedProperties = new PropertyDefinitions()
            };

            IProcessDefinition processDefinition = processMetadataCatalog.GetProcessDefinition(
                request.ApplicationId,
                1,
                request.ProcessDefinitionId);
            if (processDefinition == null)
                throw new ProcessDefinitionNotFoundException(resolver.Resolve<IResourceManager>(), request.ProcessDefinitionId);

            response.ProcessDefinition = typeAdapter.Adapt<IProcessDefinition, ProcessDefinitionDTO>(processDefinition);

            if (processDefinition.DefinedProperties != null && processDefinition.DefinedProperties.Count > 0)
            {
                foreach (IPropertyMapping mapping in processDefinition.DefinedProperties)
                {
                    response.DefinedProperties.Add(
                        typeAdapter.Adapt<IPropertyMapping, PropertyDefinitionDTO>(mapping)
                    );
                }
            }

            logger.Debug("Retrieving process definition {0} completed...", request.ProcessDefinitionId);

            return response;
        }

        public override RetrieveAvailableProcessDefinitionsResponse RetrieveAvailableProcessDefinitions(RetrieveAvailableProcessDefinitionsRequest request)
        {
            logger.Debug("Retrieving process definitions for application {0} and group {1}...", request.ApplicationId, request.ProcessDefinitionGroupId);

            RetrieveAvailableProcessDefinitionsResponse response = new RetrieveAvailableProcessDefinitionsResponse()
            {
                AvailableProcessDefinitions = new ProcessDefinitions(),
            };

            IList<IProcessDefinition> availableProcessDefinitions = processMetadataCatalog.GetAvailableProcessDefinitions(
                request.ApplicationId,
                1
            );

            if (availableProcessDefinitions != null && availableProcessDefinitions.Count > 0)
            {
                foreach (IProcessDefinition processDefinition in availableProcessDefinitions)
                {
                    response.AvailableProcessDefinitions.Add(
                        processDefinition.Name,
                        typeAdapter.Adapt<IProcessDefinition, ProcessDefinitionDTO>(processDefinition)
                    );
                }
            }

            logger.Debug("Retrieving process definitions for application {0} and group {1} completed...", request.ApplicationId, request.ProcessDefinitionGroupId);

            return response;
        }

        #endregion
    }
}
